package com.suarte.webapp.action;

import com.suarte.core.*;
import com.suarte.core.Invoice;
import com.suarte.core.service.*;
import com.suarte.utils.MathUtils;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.faces.component.UIParameter;
import org.appfuse.service.GenericManager;
import javax.faces.context.FacesContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.ActionEvent;
import javax.faces.event.ValueChangeEvent;
import javax.servlet.http.HttpServletRequest;
import org.appfuse.model.User;

/**
 * @date Mar 07, 2011
 * @author Gcastillo
 */
public class PaymentForm extends BasePage implements Serializable {

    private PaymentManager paymentManager;
    private GenericManager<Currency, Long> currencyManager;
    private CompanyManager companyManager;
    private GenericManager<PaymentType, Long> paymentTypeManager;
    private ExchangeRateManager exchangeRateManager;
    private InvoiceManager invoiceManager;
    private Payment payment = new Payment();
    private Long id;
    private Company company;
    private List<Company> companies;
    private User requestUser;
    private Boolean isView = false;
    private List<Invoice> invoices = new ArrayList();
    private Float pendingAmount = 0f;
    private Float localPendingAmount = 0f;

    public PaymentForm() {
    }

    public void setPaymentManager(PaymentManager paymentManager) {
        this.paymentManager = paymentManager;
    }

    public void setCurrencyManager(GenericManager<Currency, Long> currencyManager) {
        this.currencyManager = currencyManager;
    }

    public void setCompanyManager(CompanyManager companyManager) {
        this.companyManager = companyManager;
    }

    public void setPaymentTypeManager(GenericManager<PaymentType, Long> paymentTypeManager) {
        this.paymentTypeManager = paymentTypeManager;
    }

    public void setExchangeRateManager(ExchangeRateManager exchangeRateManager) {
        this.exchangeRateManager = exchangeRateManager;
    }

    public void setInvoiceManager(InvoiceManager invoiceManager) {
        this.invoiceManager = invoiceManager;
    }

    public Payment getPayment() {
        return payment;
    }

    public void setPayment(Payment payment) {
        this.payment = payment;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String add() {
        companies = companyManager.findCompanies();
        Date date = new Date();
        Currency currency = currencyManager.get(2L); // Default currency
        if (id != null) {
            payment = paymentManager.get(id);
        } else {
            payment = new Payment();
            payment.setDate(date);
            payment.setCurrency(currency);

            ExchangeRate exchangeRate = exchangeRateManager.findByDate(date, currency, null, ExchangeRateType.DIARIA);

            if (exchangeRate == null) {
                exchangeRate = exchangeRateManager.findByDate(date, currency, null, ExchangeRateType.MENSUAL);
            }
            if (exchangeRate == null) {
                exchangeRate = exchangeRateManager.findByDate(date, currency, null, ExchangeRateType.ANUAL);
            }
            if (exchangeRate != null) {
                payment.setExchangeRate(exchangeRate.getRate());
            } else {
                payment.setExchangeRate(1f);
            }
        }

        isView = false;
        return "view";
    }

    public String delete() {
        paymentManager.remove(payment.getId());
        addMessage("payment.deleted");

        return "list";
    }

    public String edit() {
        System.out.println("Entra");
        Date date = new Date();
        companies = companyManager.findCompanies();
        Currency currency = currencyManager.get(2L); // Default currency
        if (id != null) {
            payment = paymentManager.get(id);
            company = payment.getCompany();
        } else {
            payment = new Payment();
            payment.setDate(date);
            payment.setCurrency(currency);
            ExchangeRate exchangeRate = exchangeRateManager.findByDate(date, currency, null, ExchangeRateType.DIARIA);

            if (exchangeRate == null) {
                exchangeRate = exchangeRateManager.findByDate(date, currency, null, ExchangeRateType.MENSUAL);
            }
            if (exchangeRate == null) {
                exchangeRate = exchangeRateManager.findByDate(date, currency, null, ExchangeRateType.ANUAL);
            }
            if (exchangeRate != null) {
                payment.setExchangeRate(exchangeRate.getRate());
            } else {
                payment.setExchangeRate(1f);
            }
        }

        isView = true;
        return "view";
    }

    public String save() {
        boolean isNew = (payment.getId() == null);
        HttpServletRequest request = getRequest();
        requestUser = userManager.getUserByUsername(request.getRemoteUser());
        Date date = new Date();
        
        if (payment.getReceiptNumber() == null) {
            String key = "payment.receiptMissing";
            addMessage(key);
            return "false";
        }

        /*
        if (payment.getExchangeRate() != null && payment.getExchangeRate().equals(1F)) {
            String key = "payment.rateMissing";
            addMessage(key);
            return "false";
        } */

        if (invoices == null || (invoices != null && invoices.isEmpty())) {
            String key = "payment.invoiceMissing";
            addMessage(key);
            return "false";
        }

        Float totalDistributed = 0F;
        for (Invoice invoice : invoices) {
            totalDistributed += invoice.getCurrentPayment();
        }
        if (!payment.getAmount().equals(totalDistributed)) {
            String key = "payment.totalIssue";
            addMessage(key);
            return "false";
        }

        for (Invoice invoice : invoices) {
            Float totalAmountPaid = 0F;

            if (invoice.getAmountPaid() != null && invoice.getCurrentPayment() != null) {
                totalAmountPaid = invoice.getAmountPaid() + invoice.getCurrentPayment();
                invoice.setAmountPaid(totalAmountPaid);
            }

            if (invoice.getAmountPaid() != null && invoice.getAmountPaid() > 0f) {
                InvoicePayment detail = new InvoicePayment();
                detail.setPayment(payment);
                detail.setInvoice(invoice);
                detail.setAmount(invoice.getCurrentPayment());

                if (payment.getCurrency() != null && payment.getCurrency().isLocal()) {
                    detail.setLocalAmount(invoice.getCurrentPayment());
                    invoice.setLocalAmountPaid(totalAmountPaid);
                    // Balance decrease with payment
                    Float companyLocalBalance = payment.getCompany().getBalanceLocal() - invoice.getLocalAmountPaid();
                    payment.getCompany().setBalanceLocal(companyLocalBalance);
                    
                    Float foreignAmount = MathUtils.round(detail.getAmount() / invoice.getExchangeRate());
                    detail.setForeignAmount(foreignAmount);
                    Float totalForeignAmount = invoice.getForeignAmountPaid() + foreignAmount;
                    invoice.setForeignAmountPaid(totalForeignAmount);
                    // Balance decrease with payment
                    Float companyBalance = payment.getCompany().getBalance() - foreignAmount;
                    payment.getCompany().setBalance(companyBalance);                    

                    Float pendingBalance = invoice.getLocalAmount() - (invoice.getLocalAmountPaid() != null ? invoice.getLocalAmountPaid() : 0f);
                    if (pendingBalance == 0f) {
                        invoice.setStatus(DocumentStatus.PAGADA);
                    } else if (pendingBalance > 0f) {
                        invoice.setStatus(DocumentStatus.PAGADA_PARCIAL);
                    }
                    foreignAmount = MathUtils.round(payment.getAmount() / invoice.getExchangeRate());
                    payment.setLocalAmount(payment.getAmount());
                    payment.setForeignAmount(foreignAmount);
                    payment.setEquivalentAmount(foreignAmount);
                } else if (payment.getCurrency() != null && !payment.getCurrency().isLocal()) {
                    detail.setForeignAmount(invoice.getCurrentPayment());
                    invoice.setForeignAmountPaid(totalAmountPaid);
                    // Balance decrease with payment
                    Float companyBalance = payment.getCompany().getBalance() - detail.getForeignAmount();
                    payment.getCompany().setBalance(companyBalance);

                    Float localAmount = MathUtils.round(detail.getAmount() * invoice.getExchangeRate());
                    detail.setLocalAmount(localAmount);
                    Float totalLocalAmount = invoice.getLocalAmountPaid() + localAmount;
                    invoice.setLocalAmountPaid(totalLocalAmount);
                    // Balance decrease with payment
                    Float companyLocalBalance = payment.getCompany().getBalanceLocal() - localAmount;
                    payment.getCompany().setBalanceLocal(companyLocalBalance);
                    
                    Float pendingBalance = invoice.getForeignAmount() - (invoice.getForeignAmountPaid() != null ? invoice.getForeignAmountPaid() : 0f);
                    if (pendingBalance == 0f) {
                        invoice.setStatus(DocumentStatus.PAGADA);
                    } else if (pendingBalance > 0f) {
                        invoice.setStatus(DocumentStatus.PAGADA_PARCIAL);
                    }
                    localAmount = MathUtils.round(payment.getAmount() * invoice.getExchangeRate());
                    payment.setForeignAmount(payment.getAmount());
                    payment.setLocalAmount(localAmount);
                    payment.setEquivalentAmount(localAmount);
                }
                companyManager.save(payment.getCompany());
                invoice.setCurrentPayment(null);
                invoiceManager.save(invoice);
                payment.addPaymentDetail(detail);
            }
        }

        payment.setCreatedBy(requestUser);
        payment.setCreatedOn(date);
        payment.setModifiedBy(requestUser);
        payment.setModifiedOn(date);
        paymentManager.save(payment);

        String key = (isNew) ? "payment.added" : "payment.updated";
        addMessage(key);

        if (isNew) {
            return "list";
        } else {
            return "edit";
        }
    }

    public String cancel() {
        payment.setStatus(PaymentStatus.ANULADO);

        List<InvoicePayment> invoicePayments = invoiceManager.findInvoiceByPayment(payment);

        for (InvoicePayment detail : invoicePayments) {
            Invoice invoice = detail.getInvoice();
            Float totalAmountPaid = 0F;

            if (payment.getCurrency() != null && payment.getCurrency().isLocal()) {
                totalAmountPaid = invoice.getLocalAmountPaid() - detail.getLocalAmount();
                invoice.setLocalAmountPaid(totalAmountPaid);
                invoice.setAmountPaid(totalAmountPaid);
                // Balance increase with anulation
                Float companyLocalBalance = payment.getCompany().getBalanceLocal() + detail.getLocalAmount();
                payment.getCompany().setBalanceLocal(companyLocalBalance);
                
                Float foreignAmount = detail.getForeignAmount();
                Float totalForeignAmount = invoice.getForeignAmountPaid() - foreignAmount;
                invoice.setForeignAmountPaid(totalForeignAmount);
                // Balance increase with anulation
                Float companyBalance = payment.getCompany().getBalance() + foreignAmount;
                payment.getCompany().setBalance(companyBalance);                

                Float pendingBalance = invoice.getLocalAmount() - (invoice.getLocalAmountPaid() != null ? invoice.getLocalAmountPaid() : 0f);
                if (pendingBalance == 0f) {
                    invoice.setStatus(DocumentStatus.PAGADA);
                } else if (pendingBalance.equals(invoice.getLocalAmount())) {
                    invoice.setStatus(DocumentStatus.ACTIVA);
                } else if (pendingBalance > 0f) {
                    invoice.setStatus(DocumentStatus.PAGADA_PARCIAL);
                }
            } else if (payment.getCurrency() != null && !payment.getCurrency().isLocal()) {
                totalAmountPaid = invoice.getForeignAmountPaid() - detail.getForeignAmount();
                invoice.setForeignAmountPaid(totalAmountPaid);
                invoice.setAmountPaid(totalAmountPaid);
                // Balance increase with anulation
                Float companyBalance = payment.getCompany().getBalance() + detail.getForeignAmount();
                payment.getCompany().setBalance(companyBalance);
                
                Float localAmount = detail.getLocalAmount();
                Float totalLocalAmount = invoice.getLocalAmountPaid() - localAmount;
                invoice.setLocalAmountPaid(totalLocalAmount);
                // Balance increase with anulation
                Float companyLocalBalance = payment.getCompany().getBalanceLocal() + localAmount;
                payment.getCompany().setBalanceLocal(companyLocalBalance);
                
                Float pendingBalance = invoice.getForeignAmount() - (invoice.getForeignAmountPaid() != null ? invoice.getForeignAmountPaid() : 0f);
                if (pendingBalance == 0f) {
                    invoice.setStatus(DocumentStatus.PAGADA);
                } else if (pendingBalance.equals(invoice.getForeignAmount())) {
                    invoice.setStatus(DocumentStatus.ACTIVA);
                } else if (pendingBalance > 0f) {
                    invoice.setStatus(DocumentStatus.PAGADA_PARCIAL);
                }
            }
            companyManager.save(payment.getCompany());
            invoice.setCurrentPayment(null);
            invoiceManager.save(invoice);
        }

        paymentManager.save(payment);
        String key = "payment.updated";
        addMessage(key);

        return "edit";
    }

    public void deleteInvoice(ActionEvent e) {
        UIParameter param = (UIParameter) e.getComponent().getChildren().get(0);
        Long index = Long.valueOf(param.getValue().toString());
        Invoice invoice = invoices.get(index.intValue());
        invoices.remove(invoice);
        localPendingAmount -= invoice.getLocalAmount() - (invoice.getLocalAmountPaid() != null ? invoice.getLocalAmountPaid() : 0f);
        pendingAmount -= invoice.getForeignAmount() - (invoice.getForeignAmountPaid() != null ? invoice.getForeignAmountPaid() : 0f);
    }
    
    public List<Currency> getCurrencies() {
        return currencyManager.getAll();
    }

    public Company getCompany() {
        return company;
    }

    public void setCompany(Company company) {
        this.company = company;
    }

    public List<Company> getCompanies() {
        return companies;
    }

    public void setCompanies(List<Company> companies) {
        this.companies = companies;
    }

    public User getRequestUser() {
        return requestUser;
    }

    public void setRequestUser(User requestUser) {
        this.requestUser = requestUser;
    }

    public Boolean getIsView() {
        return isView;
    }

    public void setIsView(Boolean isView) {
        this.isView = isView;
    }

    public List<Invoice> getInvoices() {
        return invoices;
    }

    public void setInvoices(List<Invoice> invoices) {
        this.invoices = invoices;
    }

    public List<PaymentType> getPaymentTypes() {
        return paymentTypeManager.getAll();
    }

    public Float getPendingAmount() {
        return pendingAmount;
    }

    public void setPendingAmount(Float pendingAmount) {
        this.pendingAmount = pendingAmount;
    }

    public Float getLocalPendingAmount() {
        return localPendingAmount;
    }

    public void setLocalPendingAmount(Float localPendingAmount) {
        this.localPendingAmount = localPendingAmount;
    }

    public void processCompanyChange(ValueChangeEvent event) throws AbortProcessingException {
        Long oldValue = event.getOldValue() != null ? ((Company) event.getOldValue()).getId() : null;
        Long newValue = event.getNewValue() != null ? ((Company) event.getNewValue()).getId() : null;
        System.out.println("Values : Old: " + oldValue + "; New: " + newValue);

        company = companyManager.get(((Company) event.getNewValue()).getId());
        payment.setCompany(company);
        invoices = invoiceManager.findPendingInvoices(company, payment.getCurrency());
        localPendingAmount = 0f;
        pendingAmount = 0f;
        for (Invoice invoice : invoices) {
            localPendingAmount += invoice.getLocalAmount() - (invoice.getLocalAmountPaid() != null ? invoice.getLocalAmountPaid() : 0f);
            pendingAmount += invoice.getForeignAmount() - (invoice.getForeignAmountPaid() != null ? invoice.getForeignAmountPaid() : 0f);
        }

        FacesContext.getCurrentInstance().renderResponse();
    }

    public void processCurrencyChange(ValueChangeEvent event) throws AbortProcessingException {
        Long oldValue = event.getOldValue() != null ? ((Currency) event.getOldValue()).getId() : null;
        Long newValue = event.getNewValue() != null ? ((Currency) event.getNewValue()).getId() : null;

        Currency currency = newValue != null ? currencyManager.get(newValue) : null;
        if (currency != null) {
            payment.setCurrency(currency);
        }
        FacesContext.getCurrentInstance().renderResponse();
    }
}
